/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package mygame;

import com.jme3.app.Application;
import com.jme3.app.state.AppStateManager;
import base.Character;
import base.Skills.Skill;
import com.jme3.animation.AnimChannel;
import com.jme3.animation.AnimControl;
import com.jme3.animation.Skeleton;
import com.jme3.animation.SkeletonControl;
import com.jme3.bullet.BulletAppState;
import com.jme3.bullet.control.BetterCharacterControl;
import com.jme3.input.ChaseCamera;
import com.jme3.input.KeyInput;
import com.jme3.input.MouseInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.AnalogListener;
import com.jme3.input.controls.JoyAxisTrigger;
import com.jme3.input.controls.JoyButtonTrigger;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.input.controls.MouseButtonTrigger;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath;
import com.jme3.math.Vector3f;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.debug.SkeletonDebugger;
import mygame.Attacks.ProjectileControl;
import mygame.procedural.ik.LegControl;
import mygame.procedural.ik.WalkIkControl;

/**
 *
 * @author Paulo
 */
public class PlayerAppState extends SimpleAppState implements ActionListener, AnalogListener {
    private Character data;
    private BetterCharacterControl control;
    private ChaseCamera chaseCam;
    
    private Node scene;
    private Spatial model;
    
    private AnimControl animControl;
    private AnimChannel anim;
    private SkeletonControl skc;
    private WalkIkControl walk;
    
    private Vector3f walkDirection = new Vector3f(0, 0, 0);
    private float airTime = 0;
    
    public PlayerAppState(Spatial scene) {
        data = new Character();
        this.scene = (Node) scene;
    }
    
    @Override
    public void initialize(AppStateManager stateManager, Application app) {
        super.initialize(stateManager, app);
        
        Skill auraDestruction = Skill.loadScript(app.getAssetManager(), "Resources/Skills/Aura/Aura Destruction.js");
        auraDestruction.setLevel(4);
        
        data.getSkills().addSkill(auraDestruction);
        data.getSkills().setSkill(auraDestruction);
        
        loadPlayer("Models/player/player.j3o", 0.2f, 1.6f);
        scene.attachChild(model);

        //debugSkeleton(animControl.getSkeleton());

        setupChaseCamera();

        setupInput();
    }
    
    private Node loadPlayer(String modelName, float collisionRadius, float collisionHeight) {
        control = new BetterCharacterControl(collisionRadius, collisionHeight, 70f);

        model = app.getAssetManager().loadModel(modelName);
        model.setLocalTranslation(0, 3, 0);
        model.addControl(control);
        
        app.getStateManager().getState(BulletAppState.class).getPhysicsSpace().add(control);

        Node skin = (Node) ((Node) model).getChild("Skin");

        animControl = skin.getControl(AnimControl.class);
        anim = animControl.createChannel();

        skc = skin.getControl(SkeletonControl.class);

        LegControl l = new LegControl();
        l.setGround(scene);
        l.setRest(new Vector3f(0.14f, -0.73f, -0.1f));
        LegControl r = new LegControl();
        r.setGround(scene);
        r.setRest(new Vector3f(-0.14f, -0.73f, -0.1f));

        Material m = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
        m.setColor("Color", ColorRGBA.Magenta);

        Node leftFoot = (Node) ((Node) model).getChild("leftFoot");
        Node rightFoot = (Node) ((Node) model).getChild("rightFoot");
        
        leftFoot.addControl(l);
        rightFoot.addControl(r);
        
        l.debug(m);
        r.debug(m);

        walk = new WalkIkControl(model, scene, leftFoot, rightFoot);

        return (Node) model;
    }
    
    private void setupChaseCamera() {
        chaseCam = new ChaseCamera(this.app.getCamera(), model, this.app.getInputManager());
        //chaseCam.setSmoothMotion(true);
        //chaseCam.setTrailingEnabled(true);
        chaseCam.setDefaultDistance(5f);
        chaseCam.setMinDistance(3f);
        chaseCam.setMaxDistance(6f);
        chaseCam.setDragToRotate(false);
        chaseCam.setMaxVerticalRotation(FastMath.PI * 1f / 3f);
        chaseCam.setMinVerticalRotation(FastMath.PI * -1f / 3f);
        chaseCam.setDownRotateOnCloseViewOnly(false);
        chaseCam.setInvertHorizontalAxis(false);
        chaseCam.setInvertVerticalAxis(true);
        chaseCam.setLookAtOffset(new Vector3f(0,2,0));
    }
    
    private void setupInput() {
        app.getInputManager().addMapping("CharLeft",
                new KeyTrigger(KeyInput.KEY_A),
                new JoyAxisTrigger(0, 1, true));

        app.getInputManager().addMapping("CharRight",
                new KeyTrigger(KeyInput.KEY_D),
                new JoyAxisTrigger(0, 1, false));

        app.getInputManager().addMapping("CharForward",
                new KeyTrigger(KeyInput.KEY_W),
                new JoyAxisTrigger(0, 0, true));

        app.getInputManager().addMapping("CharBackward",
                new KeyTrigger(KeyInput.KEY_S),
                new JoyAxisTrigger(0, 0, false));

        app.getInputManager().addMapping("CharJump",
                new KeyTrigger(KeyInput.KEY_SPACE),
                new JoyButtonTrigger(0, 0));
        
        app.getInputManager().addMapping("CharAttack", 
                new MouseButtonTrigger(MouseInput.BUTTON_LEFT),
                new JoyButtonTrigger(7, 7));

        app.getInputManager().addListener(this, "CharLeft", "CharRight", 
                "CharForward", "CharBackward", "CharJump", "CharAttack");
    }
    
    private void debugSkeleton(Skeleton skeleton) {
        SkeletonDebugger skeletonDebug = new SkeletonDebugger("skeleton", skeleton);
        Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
        mat.setColor("Color", ColorRGBA.Green);
        mat.getAdditionalRenderState().setDepthTest(false);
        skeletonDebug.setMaterial(mat);
        ((Node) model).attachChild(skeletonDebug);
    }
    
    private void addFireball(Vector3f direction) {
        Spatial fb = app.getAssetManager().loadModel("Models/attack_effects/fire_ball.j3o");
        ProjectileControl ct = new ProjectileControl();
        ct.setVelocity(direction);
        fb.addControl(ct);
        
        fb.setLocalTranslation(model.getLocalTranslation().add(Vector3f.UNIT_Y));
        app.getRootNode().attachChild(fb);
    }
    
    @Override
    public void update(float tpf) {
        //bullet.setDebugEnabled(true);
        Vector3f camDir = app.getCamera().getDirection().clone();
        Vector3f camLeft = app.getCamera().getLeft().clone();
        camDir.y = 0;
        camLeft.y = 0;
        walkDirection.set(0, 0, 0);

        float deadZone = 0.2f;

        if (left > deadZone) {
            walkDirection.addLocal(camLeft.mult(left));
        }
        if (right > deadZone) {
            walkDirection.addLocal(camLeft.negate().mult(right));
        }
        if (up > deadZone) {
            walkDirection.addLocal(camDir.mult(up));
        }
        if (down > deadZone) {
            walkDirection.addLocal(camDir.negate().mult(down));
        }

        if (!control.isOnGround()) {
            airTime = airTime + tpf;
        } else {
            airTime = 0;
        }

        /* animations
         if (walkDirection.length() == 0) {
         if (!"Idle".equals(anim.getAnimationName())) {
         anim.setAnim("Idle", 1f);
         }
         } else {
         control.setViewDirection(walkDirection);
         if (airTime > .3f) {
         if (!"Idle".equals(anim.getAnimationName())) {
         anim.setAnim("Idle");
         }
         } else if (!"WalkForward".equals(anim.getAnimationName())) {
         anim.setAnim("WalkForward", 0.7f);
         }
         }
         * */

        if (walkDirection.length() > 0f) {
            //control.setViewDirection(walkDirection);
            control.setViewDirection(camDir);
        }
        control.setWalkDirection(walkDirection);
        walk.update(tpf);

        left = right = up = down = 0;
    }

    private float left = 0f;
    private float right = 0f;
    private float up = 0f;
    private float down = 0f;

    @Override
    public void onAction(String binding, boolean value, float tpf) {
        if (binding.equals("CharJump") && value) {
            control.jump();
        } else if (binding.equals("CharAttack") && value) {
            addFireball(app.getCamera().getDirection().mult(10));
        }
    }

    public void onAnalog(String name, float value, float tpf) {
        if (name.equals("CharLeft")) {
            left = value / tpf;
        } else if (name.equals("CharRight")) {
            right = value / tpf;
        } else if (name.equals("CharForward")) {
            up = value / tpf;
        } else if (name.equals("CharBackward")) {
            down = value / tpf;
        }
    }
    
    @Override
    public void cleanup() {
        super.cleanup();
        
        model.removeFromParent();
        
        app.getInputManager().deleteMapping("CharLeft");
        app.getInputManager().deleteMapping("CharRight");
        app.getInputManager().deleteMapping("CharForward");
        app.getInputManager().deleteMapping("CharBackward");
        app.getInputManager().deleteMapping("CharJump");
        app.getInputManager().deleteMapping("CharAttack");
        app.getInputManager().removeListener(this);
    }

}
